using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Text.RegularExpressions;

// doc: https://gist.github.com/FabienDehopre/5245476

namespace Ycsd.Core
{
    public static partial class CSHelper
    {
        public static readonly HashSet<string> CsKeywords;

        // definition of a valid C# identifier: http://msdn.microsoft.com/en-us/library/aa664670(v=vs.71).aspx
        private const string FORMATTING_CHARACTER = @"\p{Cf}";
        private const string CONNECTING_CHARACTER = @"\p{Pc}";
        private const string DECIMAL_DIGIT_CHARACTER = @"\p{Nd}";
        private const string COMBINING_CHARACTER = @"\p{Mn}|\p{Mc}";
        private const string LETTER_CHARACTER = @"\p{Lu}|\p{Ll}|\p{Lt}|\p{Lm}|\p{Lo}|\p{Nl}";

        private const string IDENTIFIER_PART_CHARACTER = LETTER_CHARACTER + "|" +
                                                         DECIMAL_DIGIT_CHARACTER + "|" +
                                                         CONNECTING_CHARACTER + "|" +
                                                         COMBINING_CHARACTER + "|" +
                                                         FORMATTING_CHARACTER;

        private const string IDENTIFIER_PART_CHARACTERS = "(" + IDENTIFIER_PART_CHARACTER + ")+";
        private const string IDENTIFIER_START_CHARACTER = "(" + LETTER_CHARACTER + "|_)";

        private const string IDENTIFIER_OR_KEYWORD = IDENTIFIER_START_CHARACTER + "(" +
                                                     IDENTIFIER_PART_CHARACTERS + ")*";

        private static readonly Regex _validIdentifierRegex =
            new Regex("^" + IDENTIFIER_OR_KEYWORD + "$", RegexOptions.Compiled);

        static CSHelper()
        {
            CsKeywords = new HashSet<string>()
            {
                "__arglist", "__makeref", "__reftype", "__refvalue",
                "abstract", "as", "base", "bool",
                "break", "byte", "case", "catch",
                "char", "checked", "class", "const",
                "continue", "decimal", "default", "delegate",
                "do", "double", "else", "enum",
                "event", "explicit", "extern", "false",
                "finally", "fixed", "float", "for",
                "foreach", "goto", "if", "implicit",
                "in", "int", "interface", "internal",
                "is", "lock", "long", "namespace",
                "new", "null", "object", "operator",
                "out", "override", "params", "private",
                "protected", "public", "readonly", "ref",
                "return", "sbyte", "sealed", "short",
                "sizeof", "stackalloc", "static", "string",
                "struct", "switch", "this", "throw",
                "true", "try", "typeof", "uint",
                "ulong", "unchecked", "unsafe", "ushort",
                "using", "virtual", "volatile", "void",
                "while"
            };
        }

        /// <summary>
        /// 获取type在源代码中的名字
        /// </summary>
        public static string SourceCode(this Type type)
        {
            if (type == null)
                throw new ArgumentNullException(nameof(type));
            if (type.IsEnum)
            {
                // 嵌套类名称中有一个"+"号，需要替换成"."
                return type.FullName.Replace("+", ".");
            }

            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Boolean:
                {
                    return "bool";
                }
                case TypeCode.Char:
                {
                    return "char";
                }
                case TypeCode.SByte:
                {
                    return "sbyte";
                }
                case TypeCode.Byte:
                {
                    return "byte";
                }
                case TypeCode.Int16:
                {
                    return "short";
                }
                case TypeCode.UInt16:
                {
                    return "ushort";
                }
                case TypeCode.Int32:
                {
                    return "int";
                }
                case TypeCode.UInt32:
                {
                    return "uint";
                }
                case TypeCode.Int64:
                {
                    return "long";
                }
                case TypeCode.UInt64:
                {
                    return "ulong";
                }
                case TypeCode.Single:
                {
                    return "float";
                }
                case TypeCode.Double:
                {
                    return "double";
                }
                case TypeCode.Decimal:
                {
                    return "decimal";
                }
                case TypeCode.DateTime:
                {
                    return "System.DateTime";
                }
                case TypeCode.String:
                {
                    return "string";
                }
                default:
                {
                    if (type.IsGenericType)
                    {
                        if (type.IsGenericTypeDefinition)
                        {
                            // 泛型定义, List<>, Dictionary<,>, Nullable<> ...
                            var fullname = type.FullName;
                            var baseName = fullname.Substring(0, fullname.IndexOf("`"));
                            var commas = new string(',', type.GetGenericArguments().Length - 1);
                            return $"{baseName}<{commas}>";
                        }
                        else
                        {
                            var typeDef = type.GetGenericTypeDefinition();
                            if (typeDef == typeof(Nullable<>))
                            {
                                return $"{type.GetGenericArguments()[0].SourceCode()}?";
                                ;
                            }
                            else
                            {
                                // 泛型
                                var fullname = type.FullName;
                                var baseName = fullname.Substring(0, fullname.IndexOf("`"));
                                var args = new List<string>();
                                foreach (var arg in type.GetGenericArguments())
                                {
                                    args.Add(arg.SourceCode());
                                }

                                return $"{baseName}<{string.Join(", ", args)}>";
                            }
                        }
                    }
                    else
                    {
                        if (type.IsArray)
                        {
                            return $"{type.GetElementType().SourceCode()}[]";
                        }

                        // 嵌套类名称中有一个"+"号，需要替换成"."
                        return type.FullName.Replace("+", ".");
                    }
                }
            }

            throw new ArgumentException("Can't get source code of the type.");
        }

        /// <summary>
        /// 判断s是C#关键字，主要用于代码生成器，避开C#关键字和保留字
        /// </summary>
        public static bool IsCsKeyword(this string s)
        {
            return CsKeywords.Contains(s);
        }

        /// <summary>
        /// 判断s是否是一个有效的类型，字段名
        /// </summary>
        public static bool IsValidIdentifier(this string identifier)
        {
            if (String.IsNullOrWhiteSpace(identifier)) return false;

            var normalizedIdentifier = identifier.Normalize();

            // 1. check that the identifier match the validIdentifer regex and it's not a C# keyword
            if (_validIdentifierRegex.IsMatch(normalizedIdentifier) && !CsKeywords.Contains(normalizedIdentifier))
            {
                return true;
            }

            // 2. check if the identifier starts with @
            if (normalizedIdentifier.StartsWith("@") &&
                _validIdentifierRegex.IsMatch(normalizedIdentifier.Substring(1)))
            {
                return true;
            }

            // 3. it's not a valid identifier
            return false;
        }

        /// <summary>
        /// 获取type的构造方式，构造方法或者静态方法
        /// </summary>
        public static MethodBase GetConstructor(Type type, Type argType)
        {
            // 优先搜索有一个参数(argType类型)的构造方法
            foreach (var c in type.GetConstructors())
            {
                var ps = c.GetParameters();
                if (ps.Length == 1 && ps[0].ParameterType.IsAssignableFrom(argType))
                {
                    return c;
                }
            }

            // 搜索有一个参数(argType类型)的静态方法
            foreach (var m in type.GetMethods(BindingFlags.Static | BindingFlags.Public))
            {
                if (m.ReturnType == type)
                {
                    var ps = m.GetParameters();
                    if (ps.Length == 1 && ps[0].ParameterType.IsAssignableFrom(argType))
                    {
                        return m;
                    }
                }
            }

            // 搜索Attribute标记的方法
            return null;
        }


        /// <summary>
        /// 搜索Assembly-CSharp
        /// </summary>
        /// <returns></returns>
        public static System.Reflection.Assembly GetAssemblyCSharp()
        {
            return AppDomain.CurrentDomain.GetAssemblies()
                .FirstOrDefault(s => s.FullName.StartsWith("Assembly-CSharp,"));
        }
    }
}